package org.bridgen.runtime.inspect;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.util.HashMap;

import javax.swing.JComponent;

import org.bridgen.runtime.RecoveryHelper;
import org.bridgen.runtime.model.EOF;
import org.bridgen.runtime.model.Island;
import org.bridgen.runtime.model.LexicalNode;
import org.bridgen.runtime.model.Reef;
import org.bridgen.runtime.model.SOF;
import org.bridgen.runtime.model.Water;

/**
 * 
 * @author Emma Söderberg <emma add cs.lth.se>
 *
 */

public class RecoveryNodePanel extends JComponent {

	private static final long serialVersionUID = 1L;
	
	private int WIDTH = 30;
	private int HEIGHT = 20;
	private static final int SQUARE_WIDTH = 15;
	private static final int SQUARE_XPAD = 3;
	private static final int BRIDGE_XPAD = 7;
	private static final int SQUARE_HEIGHT = 17;
	private static final int SQUARE_YPAD = 13;
	private static final int BRIDGE_YPAD = 8;
	private static final int XMARGIN = 25;
	private final Color gridColor = new Color(231,228,228);
	private final Color textColor = new Color(128,128,128);
	private final Color waterColor = new Color(189,231,246);
	private final Color islandColor = new Color(180,224,169);
	private final Color reefColor = new Color(163,212,201);
	private final Color limitColor = Color.orange;
	
	private SOF sof;
	private HashMap<LexicalNode, Point> nodePointMap;
	
	public RecoveryNodePanel() {
		setPreferredSize(new Dimension((WIDTH+20)*SQUARE_WIDTH,
				(HEIGHT+30)*SQUARE_HEIGHT));
		nodePointMap = new HashMap<LexicalNode, Point>();
	}
	
	public void displayNodes(SOF sof) {
		this.sof = sof;
		setPreferredSize(new Dimension(WIDTH*SQUARE_WIDTH,
				HEIGHT*SQUARE_HEIGHT));
		revalidate();
		repaint();
	}
	
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		Font font = new Font("Serif", Font.PLAIN, 12);
		g.setFont(font);
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, WIDTH*SQUARE_WIDTH + XMARGIN, HEIGHT*SQUARE_HEIGHT);

		paintGrid(g);
		if (sof != null) {
			paintNodes(g);
		}	
		if (sof != null) {
			paintBridges(g);
		} 
	}
	
	private void paintGrid(Graphics g) {
		int line = 1;
		for (int y = 0; y < HEIGHT*SQUARE_HEIGHT; y+=SQUARE_HEIGHT) {
			g.setColor(textColor);
			g.drawString(String.valueOf(line++), SQUARE_XPAD, y + SQUARE_YPAD);
			g.setColor(gridColor);
			for (int x = 0; x < WIDTH*SQUARE_WIDTH; x+=SQUARE_WIDTH) {
				g.drawRect(XMARGIN + x,y,SQUARE_WIDTH,SQUARE_HEIGHT);
			}
		}
	}
	
	private void paintNodes(Graphics g) {
		LexicalNode node = sof;
		boolean paint = false;
		boolean fake = false;
		int x = 0;
		int y = 0;
		while (node != null) {
			/* DEBUG System.out.println("Checking node: " + node); */
						
			Color nodeColor = waterColor;
			if (node instanceof Water) {
				paint = true;
			} else if (node instanceof Island) {
				nodeColor = islandColor;
				paint = true;
				if (((Island) node).isFake()) {
					fake = true;
				}
			} else if (node instanceof Reef) {
				nodeColor = reefColor;
				paint = true;
			}
			
			if (fake) {
				
				g.setColor(Color.RED);
				g.fillRect(x*SQUARE_WIDTH + XMARGIN - 2, 
						y*SQUARE_HEIGHT, SQUARE_WIDTH/2, SQUARE_HEIGHT);
				fake = false;
				paint = false;
				nodePointMap.put(node, 
						new Point(x*SQUARE_WIDTH + XMARGIN - 1, 
						y*SQUARE_HEIGHT + SQUARE_HEIGHT/2));
				node = node.getNext();
				continue;
			}
			nodePointMap.put(node, 
					new Point(x*SQUARE_WIDTH + XMARGIN + SQUARE_WIDTH/2,
							y*SQUARE_HEIGHT + SQUARE_HEIGHT/2));
			
			char[] v = node.getValue().toCharArray();
			for (int i = 0; i < v.length; i++) {
					
				if (paint) {
					g.setColor(nodeColor);
					g.fillRect(x*SQUARE_WIDTH + XMARGIN, 
							y*SQUARE_HEIGHT, SQUARE_WIDTH, SQUARE_HEIGHT);
				}
				g.setColor(textColor);
				g.drawChars(v, i, 1, 
						x*SQUARE_WIDTH + SQUARE_XPAD + XMARGIN,
						y*SQUARE_HEIGHT + SQUARE_YPAD);
				
				if (v[i] == '\n') {
					x = 0;
					y++;
				} else {
					x++;
				}
			}
			
			paint = false;
			node = node.getNext();
		} 
	}
	
	private void underlineIsland(Graphics g,Island island, Color c) {
		Point p = nodePointMap.get(island);
		if (p == null) {
			System.err.println("ERROR: Unknown position of island");
		} else {
			g.setColor(c);
			int startX = (int)p.getX() - SQUARE_WIDTH/2;
			int startY = (int)p.getY() + SQUARE_HEIGHT/2;
			int endX = startX + SQUARE_WIDTH;
			int endY = startY;
			g.drawLine(startX, startY, endX, endY);
		}
	}
	
	private void drawBridge(Graphics g, Island start, Island end) {
		Color c = start.isFake() || end.isFake() ? Color.RED : Color.BLACK;
		Point pStart = nodePointMap.get(start);
		Point pEnd = nodePointMap.get(end);
		if (pStart == null || pEnd == null) {
			System.err.println("ERROR: Unknown position of end of bridge");
		} else {
			g.setColor(c);
			int startX = (int)pStart.getX();
			int startY = (int)pStart.getY();
			int endX = (int)pEnd.getX();
			int endY = (int)pEnd.getY();
			g.drawLine(startX, startY, endX, endY);
		}
	}
	
	
	private void paintBridges(Graphics g) {
		Island island = RecoveryHelper.nextIsland(sof);
		while (!(island instanceof EOF)) {
			if (!island.hasBridge()) {
				underlineIsland(g, island, Color.RED);
			} else if (island.startOfBridge() && island.hasBridge()) {
				Island bridgeEnd = island.getBridge().getEnd();
				drawBridge(g, island, bridgeEnd);
			}
			island = RecoveryHelper.nextIsland(island);
		} 
	}
	
}
